Product
Socket Now Supports uv.lock Files
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.
interface-datastore
Advanced tools
The `interface-datastore` npm package provides a set of interfaces and utilities for creating and working with datastores. It is designed to be used as a base for implementing various types of datastores, such as in-memory, file-based, or networked datastores. The package is part of the IPFS (InterPlanetary File System) project and is used to standardize how data is stored and retrieved.
Basic Operations
This feature allows you to perform basic CRUD (Create, Read, Update, Delete) operations on a datastore. The example demonstrates how to put, get, and delete a value in an in-memory datastore.
const { MemoryDatastore } = require('interface-datastore');
const datastore = new MemoryDatastore();
// Put a value
await datastore.put(new Key('/example'), Buffer.from('Hello, world!'));
// Get a value
const value = await datastore.get(new Key('/example'));
console.log(value.toString()); // 'Hello, world!'
// Delete a value
await datastore.delete(new Key('/example'));
Querying
This feature allows you to query the datastore for entries that match certain criteria. The example demonstrates how to put multiple values and then query the datastore for entries with a specific prefix.
const { MemoryDatastore } = require('interface-datastore');
const datastore = new MemoryDatastore();
// Put some values
await datastore.put(new Key('/example/1'), Buffer.from('Value 1'));
await datastore.put(new Key('/example/2'), Buffer.from('Value 2'));
await datastore.put(new Key('/example/3'), Buffer.from('Value 3'));
// Query the datastore
const query = datastore.query({ prefix: '/example' });
for await (const { key, value } of query) {
console.log(key.toString(), value.toString());
}
Batch Operations
This feature allows you to perform multiple operations in a single batch, which can be more efficient than performing them individually. The example demonstrates how to create a batch, add operations to it, and then commit the batch.
const { MemoryDatastore } = require('interface-datastore');
const datastore = new MemoryDatastore();
// Create a batch
const batch = datastore.batch();
// Add operations to the batch
batch.put(new Key('/example/1'), Buffer.from('Value 1'));
batch.put(new Key('/example/2'), Buffer.from('Value 2'));
batch.delete(new Key('/example/3'));
// Commit the batch
await batch.commit();
The `level` package provides a simple, efficient, and flexible interface for working with LevelDB, a fast key-value storage library. It offers similar basic operations and querying capabilities but is specifically designed for use with LevelDB.
The `nedb` package is a lightweight, in-memory database with a MongoDB-like API. It offers similar CRUD operations and querying capabilities but is designed to be used as an embedded database for small applications.
The `pouchdb` package is a JavaScript database that syncs with CouchDB. It offers similar CRUD operations and querying capabilities but is designed for offline-first applications and synchronization with CouchDB.
Implementation of the datastore interface in JavaScript
has(key, [options])
-> Promise<Boolean>
put(key, value, [options])
-> Promise
putMany(source, [options])
-> AsyncIterator<{ key: Key, value: Uint8Array }>
get(key, [options])
-> Promise<Uint8Array>
getMany(source, [options])
-> AsyncIterator<Uint8Array>
delete(key, [options])
-> Promise
deleteMany(source, [options])
-> AsyncIterator<Key>
query(query, [options])
-> AsyncIterable<Uint8Array>
batch()
open()
-> Promise
close()
-> Promise
src/memory
datastore-level
(supports any levelup compatible backend)datstore-fs
datastore-core/src/mount
datstore-core/src/keytransform
datastore-core/src/sharding
datstore-core/src/tiered
datastore-core/src/namespace
If you want the same functionality as go-ds-flatfs, use sharding with fs.
const FsStore = require('datastore-fs')
const ShardingStore = require('datastore-core').ShardingDatatstore
const NextToLast = require('datastore-core').shard.NextToLast
const fs = new FsStore('path/to/store')
// flatfs now works like go-flatfs
const flatfs = await ShardingStore.createOrOpen(fs, new NextToLast(2))
An adapter is made available to make implementing your own datastore easier:
const { Adapter } = require('interface-datastore')
class MyDatastore extends Adapter {
constructor () {
super()
}
async put (key, val) {
// your implementation here
}
async get (key) {
// your implementation here
}
// etc...
}
See the MemoryDatastore for an example of how it is used.
$ npm install interface-datastore
The type definitions for this package are available on http://definitelytyped.org/. To install just use:
$ npm install -D @types/interface-datastore
const MemoryStore = require('interface-datastore').MemoryDatastore
const MountStore = require('datastore-core').MountDatastore
const Key = require('interface-datastore').Key
const store = new MountStore({ prefix: new Key('/a'), datastore: new MemoryStore() })
Available under src/tests.js
describe('mystore', () => {
require('interface-datastore/src/tests')({
async setup () {
return instanceOfMyStore
},
async teardown () {
// cleanup resources
}
})
})
### Aborting requests
Most API methods accept an AbortSignal as part of an options object. Implementations may listen for an abort
event emitted by this object, or test the signal.aborted
property. When received implementations should tear down any long-lived requests or resources created.
### Concurrency
The streaming (put|get|delete)Many
methods are intended to be used with modules such as it-parallel-batch to allow calling code to control levels of parallelisation. The batching method ensures results are returned in the correct order, but interface implementations should be thread safe.
const batch = require('it-parallel-batch')
const source = [{
key: ..,
value: ..
}]
// put values into the datastore concurrently, max 10 at a time
for await (const { key, data } of batch(store.putMany(source), 10)) {
console.info(`Put ${key}`)
}
To allow a better abstraction on how to address values, there is a Key
class which is used as identifier. It's easy to create a key from a Uint8Array
or a string
.
const a = new Key('a')
const b = new Key(new Uint8Array([0, 1, 2, 3]))
The key scheme is inspired by file systems and Google App Engine key model. Keys are meant to be unique across a system. They are typically hierarchical, incorporating more and more specific namespaces. Thus keys can be deemed 'children' or 'ancestors' of other keys:
new Key('/Comedy')
new Key('/Comedy/MontyPython')
Also, every namespace can be parameterized to embed relevant object information. For example, the Key name
(most specific namespace) could include the object type:
new Key('/Comedy/MontyPython/Actor:JohnCleese')
new Key('/Comedy/MontyPython/Sketch:CheeseShop')
new Key('/Comedy/MontyPython/Sketch:CheeseShop/Character:Mousebender')
Implementations of this interface should make the following methods available:
has(key, [options])
-> Promise<Boolean>
Check for the existence of a given key
Name | Type | Description |
---|---|---|
key | Key | The key to check the existance of |
options | Object | An options object, all properties are optional |
options.signal | AbortSignal | A way to signal that the caller is no longer interested in the outcome of this operation |
const exists = await store.has(new Key('awesome'))
if (exists) {
console.log('it is there')
} else {
console.log('it is not there')
}
put(key, value, [options])
-> Promise
Store a value with the given key.
Name | Type | Description |
---|---|---|
key | Key | The key to store the value under |
value | Uint8Array | Value to store |
options | Object | An options object, all properties are optional |
options.signal | AbortSignal | A way to signal that the caller is no longer interested in the outcome of this operation |
await store.put([{ key: new Key('awesome'), value: new Uint8Array([0, 1, 2, 3]) }])
console.log('put content')
putMany(source, [options])
-> AsyncIterator<{ key: Key, value: Uint8Array }>
Store many key-value pairs.
Name | Type | Description |
---|---|---|
source | AsyncIterator<{ key: Key, value: Uint8Array }> | The key to store the value under |
value | Uint8Array | Value to store |
options | Object | An options object, all properties are optional |
options.signal | AbortSignal | A way to signal that the caller is no longer interested in the outcome of this operation |
const source = [{ key: new Key('awesome'), value: new Uint8Array([0, 1, 2, 3]) }]
for await (const { key, value } of store.putMany(source)) {
console.info(`put content for key ${key}`)
}
get(key, [options])
-> Promise<Uint8Array>
Name | Type | Description |
---|---|---|
key | Key | The key retrieve the value for |
options | Object | An options object, all properties are optional |
options.signal | AbortSignal | A way to signal that the caller is no longer interested in the outcome of this operation |
Retrieve the value stored under the given key.
const value = await store.get(new Key('awesome'))
console.log('got content: %s', value.toString('utf8'))
// => got content: datastore
getMany(source, [options])
-> AsyncIterator<Uint8Array>
Name | Type | Description |
---|---|---|
source | AsyncIterator<Key> | One or more keys to retrieve values for |
options | Object | An options object, all properties are optional |
options.signal | AbortSignal | A way to signal that the caller is no longer interested in the outcome of this operation |
Retrieve a stream of values stored under the given keys.
for await (const value of store.getMany([new Key('awesome')])) {
console.log('got content:', new TextDecoder('utf8').decode(value))
// => got content: datastore
}
delete(key, [options])
-> Promise
Delete the content stored under the given key.
Name | Type | Description |
---|---|---|
key | Key | The key to remove the value for |
options | Object | An options object, all properties are optional |
options.signal | AbortSignal | A way to signal that the caller is no longer interested in the outcome of this operation |
await store.delete(new Key('awesome'))
console.log('deleted awesome content :(')
deleteMany(source, [options])
-> AsyncIterator<Key>
Delete the content stored under the given keys.
Name | Type | Description |
---|---|---|
source | AsyncIterator<Key> | One or more keys to remove values for |
options | Object | An options object, all properties are optional |
options.signal | AbortSignal | A way to signal that the caller is no longer interested in the outcome of this operation |
const source = [new Key('awesome')]
for await (const key of store.deleteMany(source)) {
console.log(`deleted content with key ${key}`)
}
query(query, [options])
-> AsyncIterable<Uint8Array>
Search the store for some values. Returns an AsyncIterable with each item being a Uint8Array.
Name | Type | Description |
---|---|---|
query | Object | A query object, all properties are optional |
query.prefix | String | Only return values where the key starts with this prefix |
query.filters | Array<Function(Uint8Array) -> Boolean> | Filter the results according to the these functions |
query.orders | Array<Function(Array<Uint8Array>) -> Array<Uint8Array>> | Order the results according to these functions |
query.limit | Number | Only return this many records |
query.offset | Number | Skip this many records at the beginning |
options | Object | An options object, all properties are optional |
options.signal | AbortSignal | A way to signal that the caller is no longer interested in the outcome of this operation |
// retrieve __all__ values from the store
let list = []
for await (const value of store.query({})) {
list.push(value)
}
console.log('ALL THE VALUES', list)
batch()
This will return an object with which you can chain multiple operations together, with them only being executed on calling commit
.
const b = store.batch()
for (let i = 0; i < 100; i++) {
b.put(new Key(`hello${i}`), new TextEncoder('utf8').encode(`hello world ${i}`))
}
await b.commit()
console.log('put 100 values')
put(key, value)
Queue a put operation to the store.
Name | Type | Description |
---|---|---|
key | Key | The key to store the value under |
value | Uint8Array | Value to store |
delete(key)
Queue a delete operation to the store.
Name | Type | Description |
---|---|---|
key | Key | The key to remove the value for |
commit([options])
-> Promise<void>
Write all queued operations to the underyling store. The batch object should not be used after calling this.
Name | Type | Description |
---|---|---|
options | Object | An options object, all properties are optional |
options.signal | AbortSignal | A way to signal that the caller is no longer interested in the outcome of this operation |
const batch = store.batch()
batch.put(new Key('to-put'), new TextEncoder('utf8').encode('hello world'))
batch.del(new Key('to-remove'))
await batch.commit()
open()
-> Promise
Opens the datastore, this is only needed if the store was closed before, otherwise this is taken care of by the constructor.
close()
-> Promise
Close the datastore, this should always be called to ensure resources are cleaned up.
PRs accepted.
Small note: If editing the Readme, please conform to the standard-readme specification.
MIT 2017 © IPFS
FAQs
datastore interface
The npm package interface-datastore receives a total of 101,974 weekly downloads. As such, interface-datastore popularity was classified as popular.
We found that interface-datastore demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.
Research
Security News
Socket researchers have discovered multiple malicious npm packages targeting Solana private keys, abusing Gmail to exfiltrate the data and drain Solana wallets.
Security News
PEP 770 proposes adding SBOM support to Python packages to improve transparency and catch hidden non-Python dependencies that security tools often miss.